package com.stu.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.stu.mapper.SpikeProductMapper;
import com.stu.pojo.Myorder;
import com.stu.pojo.MyorderDetail;
import com.stu.pojo.SpikeProduct;
import com.stu.service.IMyorderDetailService;
import com.stu.service.ISpikeProductService;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Service;

import jakarta.annotation.PostConstruct;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
public class SpikeProductServiceImpl implements ISpikeProductService {
    @Autowired
    private SpikeProductMapper spikeProductMapper;
    @Autowired
    private IMyorderDetailService myorderDetailService;
    @Autowired
    private RedisTemplate<String,Object> redisTemplate;
    private DefaultRedisScript<Long> script;
    //初始化加载lua脚本文件
    @PostConstruct
    public void init(){
        script = new DefaultRedisScript<>();
        script.setResultType(Long.class);
        script.setScriptSource(new ResourceScriptSource(new ClassPathResource("niceyoo.lua")));
    }

    @Override
    public List<SpikeProduct> getAll() {
        QueryWrapper<SpikeProduct> wrapper = new QueryWrapper<>();
        Page<SpikeProduct> page =  new Page<>(1,4);
        Page<SpikeProduct> spikeProductPage = spikeProductMapper.selectPage(page, null);
        List<SpikeProduct> productList = spikeProductPage.getRecords();
        System.out.println("查询到的所有的（前4的）商品信息---------");
        productList.forEach(System.out::println);
        return productList;
    }

    @Override
    public SpikeProduct getBySId(Integer sId) {
        String key = "session_"+sId;
        ValueOperations<String, Object> operations = redisTemplate.opsForValue();
        Boolean hasKey = redisTemplate.hasKey(key);
        if(hasKey) {
            SpikeProduct spikeProduct = (SpikeProduct) operations.get(key);
            return spikeProduct;
        }else {
            SpikeProduct spikeProduct = spikeProductMapper.getBySId(sId);
            operations.set(key,spikeProduct,24, TimeUnit.HOURS);
            return spikeProduct;
        }
    }

    @Override
    public SpikeProduct getById(Integer SpikeProId) {
        String key = "Spikeproduct_"+SpikeProId;
        ValueOperations<String, Object> operations = redisTemplate.opsForValue();
        Boolean hasKey = redisTemplate.hasKey(key);
        if(hasKey) {
            SpikeProduct spikeProduct = (SpikeProduct) operations.get(key);
            return spikeProduct;
        }else {
            SpikeProduct spikeProduct = spikeProductMapper.selectById(SpikeProId);
            operations.set(key,spikeProduct,24, TimeUnit.HOURS);
            return spikeProduct;
        }
    }

    @Override
    public SpikeProduct getByProId(Integer proId) {
        QueryWrapper<SpikeProduct> wrapper = new QueryWrapper<>();
        wrapper.eq("product_id",proId);
        return spikeProductMapper.selectOne(wrapper);
    }

    //redis分布式锁
    @Override
    public Boolean decSpikeProductNumInRedis(Integer proId) {
        //锁名称
        String key ="lock_"+proId;
        //声明变量UUID
        String uuid = UUID.randomUUID().toString().replace("-", "");
        //获取锁
        Boolean flag = redisTemplate.opsForValue().setIfAbsent(key, uuid, 100, TimeUnit.MILLISECONDS);
        //System.out.println(flag);
        if(flag){
            System.out.println("抢到锁");
            SpikeProduct spikeProduct = (SpikeProduct) redisTemplate.opsForValue().get("Spikeproduct_" + proId);
            System.out.println(spikeProduct);
            if(spikeProduct==null) {
                return false;
            }
            Integer numbers = spikeProduct.getSpikeProductNums();
            if(numbers==0) {
                System.out.println(Thread.currentThread().getName() + "---没有商品了");
                return false;
            } else {
                System.out.println("剩余秒杀商品数量："+(spikeProduct.getSpikeProductNums()-1));
                spikeProduct.setSpikeProductNums(spikeProduct.getSpikeProductNums()-1);
                redisTemplate.opsForValue().set("Spikeproduct_" + proId,spikeProduct,24,TimeUnit.HOURS);
                Long execute = (Long) redisTemplate.execute(script, Arrays.asList(key),uuid);
                System.out.println("execute执行结果，1表示执行del，0表示未执行 ===== " + execute);
                return true;
            }
        }else {
//            System.out.println("获取锁失败!");
            try {
                //获取锁失败、每隔0.1秒再获取
                Thread.sleep(100);
                decSpikeProductNumInRedis(proId);
                SpikeProduct spikeProduct = (SpikeProduct) redisTemplate.opsForValue().get("Spikeproduct_" + proId);
                if(spikeProduct.getSpikeProductNums()==0)
                    return false;
                else
                    return true;
            } catch (InterruptedException e) {
                e.printStackTrace();
                return false;
            }
        }
    }





    //乐观锁修改mysql库存
    @RabbitHandler
    @RabbitListener(queues = "decProductNumQueue")
    public void decSpikeProductNumInMysql(Myorder myorder, Channel channel , Message message){
        Integer myorderId = myorder.getMyorderId();
        List<MyorderDetail> detailList = myorderDetailService.getByMyorderId(myorderId);
        for (MyorderDetail myorderDetail : detailList) {
            decMethod(myorderDetail);
        }
        try {
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
        } catch (IOException e) {
            e.printStackTrace();
            try {
                channel.basicNack(message.getMessageProperties().getDeliveryTag(),false,true);
            } catch (IOException ioException) {
                ioException.printStackTrace();
            }
        }

    }
    public void decMethod(MyorderDetail myorderDetail){
        Integer productId = myorderDetail.getProductId();
        SpikeProduct spikeProduct = getByProId(productId);
        //乐观锁
        Integer version = spikeProduct.getVersion();
        //秒杀商品Id
        Integer spikeProductId = spikeProduct.getSpikeProductId();
        QueryWrapper<SpikeProduct> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("version",version);
        queryWrapper.eq("spike_product_id",spikeProductId);
        //剩余秒杀商品数量
        Integer spikeProductNums = spikeProduct.getSpikeProductNums();
        if(spikeProductNums>0){
            spikeProduct.setSpikeProductNums(spikeProduct.getSpikeProductNums()-1);
            spikeProduct.setVersion(version+1);
            int rows = spikeProductMapper.update(spikeProduct, queryWrapper);
            if(rows>0){
                System.out.println("修改mysql库存成功");
            }else {
                decMethod(myorderDetail);
            }
        }else {
            System.out.println("卖完了！");
        }
    }
}
